Conversation
누락된 ErrorScope.Login + Dialog 에러 처리 적용
Walkthrough로그인 및 공통 에러 처리 흐름을 토스트 기반 사이드 이펙트에서 중앙화된 에러 다이얼로그 흐름으로 전환했습니다. ErrorDialogSpec에 선택적 제목이 추가되고 ErrorScope에 AUTH_SESSION_EXPIRED가 도입됐으며, HandleException과 여러 Presenter에서 다이얼로그 호출 및 로그인 재유도 로직이 사용되도록 변경되었습니다. KakaoLogin 사이드이펙트는 인스턴스화 가능한 클래스가 되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Presenter
participant Server
participant HandleException
participant ErrorEventBus
participant MainActivity
User->>Presenter: 요청 (로그인/데이터)
Presenter->>Server: API 호출
Server-->>Presenter: 예외 응답
Presenter->>HandleException: postErrorDialog(exception, ErrorScope)
Note over HandleException: 예외 타입 + 스코프 기반\n(title, message) 결정
HandleException->>ErrorEventBus: ErrorEvent.ShowDialog(ErrorDialogSpec)
ErrorEventBus->>MainActivity: ErrorDialogSpec 전달
MainActivity->>User: ReedDialog(title?, description, confirm)
alt AUTH_SESSION_EXPIRED -> 재로그인 필요
MainActivity->>Presenter: onConfirm -> onLoginRequired (resetRoot LoginScreen)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@easyhooon Figma 보니까 Dialog 타이틀도 필요하겠네요. 요건 제가 작업해서 넣겠습니다~!
|
@seoyoon513 이 브랜치에서 마저 작업하고 끝내면 될거같슴다 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt (1)
60-94: 에러 메시지들을 string resource로 관리해야 합니다.로직과 구조는 훌륭하지만, 다이얼로그 타이틀과 메시지들이 하드코딩되어 있어 i18n과 유지보수 측면에서 개선이 필요합니다:
- Line 62: "네트워크 연결이 불안정합니다.\n인터넷 연결을 확인해주세요"
- Line 68: "알 수 없는 문제가 발생했어요.\n다시 시도해주세요"
- Line 72: "로그인 오류"
- Line 73: "예기치 않은 오류가 발생했습니다.\n다시 로그인 해주세요."
- Line 77: "세션이 만료되었어요.\n다시 로그인 해주세요"
- Line 83: "알 수 없는 문제가 발생했어요.\n다시 시도해주세요"
이러한 문자열들을
strings.xml로 이동하면 다국어 지원과 일관된 문구 관리가 용이해집니다.예시:
+// strings.xml에 추가 +<string name="error_network_connection">네트워크 연결이 불안정합니다.\n인터넷 연결을 확인해주세요</string> +<string name="error_login_title">로그인 오류</string> +<string name="error_login_message">예기치 않은 오류가 발생했습니다.\n다시 로그인 해주세요.</string> +<string name="error_session_expired">세션이 만료되었어요.\n다시 로그인 해주세요</string> +<string name="error_unknown">알 수 없는 문제가 발생했어요.\n다시 시도해주세요</string>그런 다음 코드에서는:
fun postErrorDialog( context: Context, // Context 파라미터 추가 필요 errorScope: ErrorScope, exception: Throwable, @StringRes buttonLabelResId: Int = R.string.confirm, action: () -> Unit = {}, ) { val (title, message) = when { exception.isNetworkError() -> { null to context.getString(R.string.error_network_connection) } exception is HttpException -> { when (errorScope) { ErrorScope.GLOBAL -> { null to context.getString(R.string.error_unknown) } ErrorScope.LOGIN -> { context.getString(R.string.error_login_title) to context.getString(R.string.error_login_message) } ErrorScope.AUTH_SESSION_EXPIRED -> { null to context.getString(R.string.error_session_expired) } } } else -> { null to context.getString(R.string.error_unknown) } } // ... }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
core/common/src/main/kotlin/com/ninecraft/booket/core/common/constants/ErrorDialogSpec.kt(1 hunks)core/common/src/main/kotlin/com/ninecraft/booket/core/common/constants/ErrorScope.kt(1 hunks)core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt(2 hunks)feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginPresenter.kt(4 hunks)feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginUiState.kt(1 hunks)feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/MainActivity.kt(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginPresenter.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci-build
🔇 Additional comments (5)
feature/main/src/main/kotlin/com/ninecraft/booket/feature/main/MainActivity.kt (1)
73-73: LGTM! 다이얼로그 타이틀 지원이 올바르게 적용되었습니다.
ErrorDialogSpec의 새로운title속성을ReedDialog에 전달하는 변경사항이 정확합니다. nullable 타입도 적절하게 처리되고 있습니다.core/common/src/main/kotlin/com/ninecraft/booket/core/common/constants/ErrorDialogSpec.kt (1)
6-6: LGTM! 타이틀 속성이 적절하게 추가되었습니다.nullable 타입과 기본값 설정으로 하위 호환성을 유지하면서 새로운 기능을 추가한 좋은 설계입니다.
feature/login/src/main/kotlin/com/ninecraft/booket/feature/login/LoginUiState.kt (1)
18-18: LGTM! Side effect 처리를 위한 중요한 변경입니다.
data object에서data class로 변경하고 UUID 기반 key를 추가한 것은 Circuit의 side effect 시스템에서 매우 중요한 수정입니다.data object는 싱글톤이므로 동일한 side effect가 여러 번 발생해도 재구성(recomposition)이 트리거되지 않지만, UUID 키를 가진data class는 매번 새로운 인스턴스를 생성하여 각 로그인 시도마다 정상적으로 side effect가 트리거됩니다.이미
ShowToast에서 사용하던 패턴과도 일관성이 있습니다.core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt (1)
26-32: LGTM! 401 에러 처리가 올바르게 개선되었습니다.세션 만료 시
AUTH_SESSION_EXPIREDscope를 사용하여 다이얼로그 기반 에러 처리를 적용한 것이 적절합니다.onLoginRequired콜백을 통해 재로그인 플로우로 연결되는 것도 좋습니다.core/common/src/main/kotlin/com/ninecraft/booket/core/common/constants/ErrorScope.kt (1)
4-4: 제거된 ErrorScope 값 사용처 확인 완료검증 결과,
BOOK_REGISTER와RECORD_REGISTER는 코드베이스 내 어디에서도 사용되지 않고 있습니다. 따라서 이들을 제거하고AUTH_SESSION_EXPIRED를 추가한 변경은 완전히 안전하며, 컴파일 오류나 참조 누락 문제가 발생하지 않습니다.
| @Immutable | ||
| sealed interface LoginSideEffect { | ||
| data object KakaoLogin : LoginSideEffect | ||
| data class KakaoLogin(private val key: String = UUID.randomUUID().toString()) : LoginSideEffect |
There was a problem hiding this comment.
@easyhooon 로그인 API 에러 처리 방식을 개선하면서 LoginSideEffect를 초기화해주는 로직이 필요해졌습니다. (안그러면 카카오 로그인 재시도 시 무한로딩 발생) 토스트메세지 이벤트처럼 UUID 값을 사용하는 방식으로 변경했는데 확인 부탁드립니다
There was a problem hiding this comment.
아 무한로딩 발생하는게 이거 때문이었군여
분명 타임아웃 시간이 지나서 로딩이 끝날때가 되었는데 계속 돌길래 뭔가했는데
There was a problem hiding this comment.
fun handleEvent(event: LoginUiEvent) {
when (event) {
is LoginUiEvent.OnKakaoLoginButtonClick -> {
isLoading = true
sideEffect = LoginSideEffect.KakaoLogin()
}- 사용자가 카카오 로그인 버튼 클릭
- sideEffect = LoginSideEffect.KakaoLogin() → RememberedEffect 실행
- 카카오 로그인 실패 → eventSink(LoginUiEvent.LoginFailure(...))
- sideEffect = LoginSideEffect.ShowToast(...) → RememberedEffect 실행 (Toast 표시)
- 사용자가 다시 카카오 로그인 버튼 클릭
- sideEffect = LoginSideEffect.KakaoLogin()
-> 문제:프로퍼티가 없는 LoginSideEffect.KakaoLogin()은 구조적으로 동일한 값으로 인식됨 - Compose가 state.sideEffect가 변경되지 않았다고 판단
- RememberedEffect가 재실행되지 않음
- 카카오 로그인이 다시 시작되지 않음
- isLoading = true만 남아 무한 로딩
There was a problem hiding this comment.
uuid 방식으로 해결할 수 있는 케이스라 괜찮은거같습니다!
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt (1)
62-68: 방어적 코딩을 위해 Guest 체크 추가를 권장합니다.에러 발생 시
handleException을 호출하여 401 에러를 처리하는 로직이 추가되었습니다.다음 사항들을 검토해주세요:
- Guest 체크 누락:
LibraryPresenter와 달리 명시적인 guest 체크가 없습니다. 현재는RememberedEffect(lines 109-113)에서 non-guest만loadHomeContent()를 호출하므로 문제가 없지만, 나중에 다른 경로에서 이 함수가 호출될 경우 guest 사용자에게도handleException이 실행될 수 있습니다. 일관성과 방어적 코딩을 위해 다음과 같은 체크를 추가하는 것을 고려해보세요:.onFailure { exception -> uiState = UiState.Error(exception) + if (userState !is UserState.Guest) { handleException( exception = exception, onError = {}, onLoginRequired = { navigator.resetRoot(LoginScreen()) }, ) + } }
- 빈 onError 람다:
LibraryPresenter와 마찬가지로onError = {}로 전달되어 401이 아닌 다른 에러들은 다이얼로그로 표시되지 않습니다. 기존 UI 에러 표시 (line 60)를 유지하면서 401만 다이얼로그로 처리하려는 의도로 보이는데, 이것이 의도한 동작이 맞는지 확인해주세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt(2 hunks)feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-28T12:25:54.058Z
Learnt from: easyhooon
PR: YAPP-Github/Reed-Android#174
File: feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt:128-133
Timestamp: 2025-08-28T12:25:54.058Z
Learning: In BookSearchPresenter.kt, when a guest user tries to register a book and is redirected to login, the bottom sheet (isBookRegisterBottomSheetVisible) and selection state (selectedBookIsbn, selectedBookStatus) are intentionally kept open/preserved so that when the user returns from login, they can continue from where they left off without losing context.
Applied to files:
feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.ktfeature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt
🧬 Code graph analysis (2)
feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt (1)
core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt (1)
handleException(19-52)
feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt (1)
core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt (1)
handleException(19-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci-build
🔇 Additional comments (3)
feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt (2)
11-11: LGTM! 필요한 import가 올바르게 추가되었습니다.중앙화된 에러 처리를 위한
handleException과 로그인 화면 내비게이션을 위한LoginScreenimport가 적절하게 추가되었습니다.Also applies to: 20-20
111-119: 네비게이션 메서드 일관성 검토가 필요합니다.Guest가 아닌 사용자에 대한 에러 처리 로직이 추가되었으며, 401 에러 발생 시 로그인 화면으로 이동하도록 구현되었습니다.
다음 사항들을 확인해주세요:
네비게이션 메서드 불일치: Line 116에서는
navigator.resetRoot(LoginScreen())을 사용하지만, Line 134에서는navigator.redirectToLogin()을 사용합니다.
- 세션 만료 시나리오 (401)에서는 상태를 완전히 초기화하는
resetRoot가 더 적절할 수 있습니다.- 하지만 일관성을 위해 두 경우 모두 같은 메서드를 사용하거나, 차이점을 코멘트로 명시하는 것을 고려해보세요.
빈 onError 람다:
onError = {}로 전달되어 401이 아닌 다른 에러들은 다이얼로그로 표시되지 않습니다. 기존 UI 에러 표시 로직 (lines 105-109)을 유지하면서 401만 다이얼로그로 처리하려는 의도로 보이는데, 이것이 의도한 동작이 맞는지 확인해주세요.feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt (1)
9-9: LGTM! 필요한 import가 올바르게 추가되었습니다.중앙화된 에러 처리를 위한
handleException과 로그인 화면 내비게이션을 위한LoginScreenimport가 적절하게 추가되었습니다.Also applies to: 17-17
| ErrorScope.LOGIN -> { | ||
| "예기치 않은 오류가 발생했습니다.\n다시 로그인 해주세요." | ||
| val loginErrorTitle = "로그인 오류" | ||
| loginErrorTitle to "예기치 않은 오류가 발생했습니다.\n다시 로그인 해주세요." |
There was a problem hiding this comment.
굳이 변수로 안빼도될거같네여
빼려면 다 빼고 안빼려면 다 안빼고 하드코딩하는게 통일성 측면에서 좋을거같슴다
|
@seoyoon513 제가 올린 PR이라 approve를 못하는 관계로
이부분만 반영 후 approve 눌러주시면 merge할게요 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt (1)
60-94: 에러 다이얼로그 처리 로직이 명확하게 개선되었습니다.title과 message를 직접 계산하여
ErrorDialogSpec을 생성하는 구조가 간결하고 이해하기 쉽습니다. 다만 유지보수성 향상을 위해 다음 사항들을 고려해보시기 바랍니다:
메시지 중복 제거: 68번과 82번 라인의 "알 수 없는 문제가 발생했어요.\n다시 시도해주세요" 메시지가 중복됩니다. 상수로 추출하면 향후 문구 수정 시 일관성을 유지할 수 있습니다.
문장 부호 일관성: 72번 라인 LOGIN 스코프 메시지만 마침표("해주세요.")로 끝나고, 나머지 메시지들은 마침표 없이 끝납니다. 사용자 경험 일관성을 위해 통일하는 것을 권장합니다.
+private const val UNKNOWN_ERROR_MESSAGE = "알 수 없는 문제가 발생했어요.\n다시 시도해주세요" + fun postErrorDialog( errorScope: ErrorScope, exception: Throwable, @StringRes buttonLabelResId: Int = R.string.confirm, action: () -> Unit = {}, ) { val (title, message) = when { exception.isNetworkError() -> { null to "네트워크 연결이 불안정합니다.\n인터넷 연결을 확인해주세요" } exception is HttpException -> { when (errorScope) { ErrorScope.GLOBAL -> { - null to "알 수 없는 문제가 발생했어요.\n다시 시도해주세요" + null to UNKNOWN_ERROR_MESSAGE } ErrorScope.LOGIN -> { - "로그인 오류" to "예기치 않은 오류가 발생했습니다.\n다시 로그인 해주세요." + "로그인 오류" to "예기치 않은 오류가 발생했습니다.\n다시 로그인 해주세요" } ErrorScope.AUTH_SESSION_EXPIRED -> { null to "세션이 만료되었어요.\n다시 로그인 해주세요" } } } else -> { - null to "알 수 없는 문제가 발생했어요.\n다시 시도해주세요" + null to UNKNOWN_ERROR_MESSAGE } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci-build
🔇 Additional comments (1)
core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt (1)
25-33: 세션 만료 처리 로직이 올바르게 구현되었습니다.401 에러 발생 시
AUTH_SESSION_EXPIRED스코프로 에러 다이얼로그를 표시하고, 사용자가 확인 버튼을 누르면onLoginRequired()를 실행하여 로그인 화면으로 유도하는 흐름이 명확하고 정확합니다.

누락된 ErrorScope.Login + Dialog 에러 처리 적용
🔗 관련 이슈
📙 작업 설명
지훈
서윤
🧪 테스트 내역 (선택)
📸 스크린샷 또는 시연 영상 (선택)
(최종)로그인 에러 다이얼로그 타이틀 적용
Reed_.mp4
로그인 세션 만료
Reed_.mp4
💬 추가 설명 or 리뷰 포인트 (선택)
지훈
서윤
@easyhooon 에러 핸들링 정책 리마인드 (연관 PR: #118 (comment))
✅ 전체 화면 에러 (화면에 데이터를 불러와 렌더링 해야하는 경우)
네트워크 에러 문구: 네트워크 연결이 불안정합니다. 인터넷 연결을 확인해주세요그 외 서버 에러 문구: 알 수 없는 문제가 발생했어요. 다시 시도해주세요✅ 토스트 메세지 (이미 화면을 보고 있는 상태에서 요청이 있는 경우)
네트워크 에러 문구: 네트워크 연결이 불안정합니다. 인터넷 연결을 확인해주세요그 외 서버 에러 문구: 서버 errorBody 파싱해서 사용✅ 다이얼로그 (특수한 상황 ex.로그인 에러, 세션 만료 에러)
Summary by CodeRabbit
변경 요약
버그 수정
개선